//
// Created by bangsun on 2022/11/11.
//

#include "include/priority_queue.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdatomic.h>
#include <linux/kernel.h>
#include <unistd.h>


PRIVATE void expand_capacity(priority_queue *pq);

PRIVATE void expand_capacity_to(priority_queue *pq, size_t expect);

void expand_capacity(priority_queue *pq) {

}

void expand_capacity_to(priority_queue *pq, size_t expect) {
    assert(pq->capacity < expect);
    pthread_mutex_lock(&pq->mutex);
//    size_t actual_cap = pq->capacity;
//    while (actual_cap < expect) {
//        actual_cap <<= 1;
//    }
//    if (actual_cap != pq->capacity) {
//        struct pq_node *new_addr = calloc(actual_cap, sizeof(pq_node));
//        memcpy(new_addr, pq->data, pq->size * sizeof(pq_node));
//        free(pq->data);
//        pq->capacity = actual_cap;
//        pq->data = new_addr;
//        printf("%p expands cap to %u\n", pq, pq->capacity);
//    }

    while (expand_deque(&pq->tmp_queue) < expect);
    pq->size = pq->tmp_queue.size;
    pq->capacity = pq->tmp_queue.capacity;
    pthread_mutex_unlock(&pq->mutex);
}


void *pqueue_poll(priority_queue *pq) {
    if (pq->positive_locker != getpid()) {
        pthread_mutex_lock(&pq->mutex);
        pq->positive_locker = getpid();
        void *ret = poll_first(&pq->tmp_queue);
        pthread_mutex_unlock(&pq->mutex);
        pq->size = pq->tmp_queue.size;
        return ret;
    } else {
        void *ret = poll_first(&pq->tmp_queue);
        pq->size = pq->tmp_queue.size;
        return ret;
    }
}


void pq_steal_item(priority_queue *pq, priority_queue *target, float ratio) {
    assert(target->size == 0);
    int size = min(pq->size, max((int) (pq->capacity * ratio), MIN_STEAL_NUM));
//    if (target->capacity < size) {
//        expand_capacity_to(target, size);
//    }
//    memcpy(target->data, pq->data, size * sizeof(struct pq_node));
//    memmove(pq->data, pq->data + size, (pq->size - size) * sizeof(struct pq_node));

    pthread_mutex_lock(&pq->mutex);
    pq->positive_locker = getpid(); // set to the stealer`s pid
    for (int i = 0; i < size; i++) {
        pqueue_add(target, 0, pqueue_poll(pq));
    }
    pthread_mutex_unlock(&pq->mutex);

}

void init_pqueue(priority_queue *pq) {
//    pq->capacity = DEFAULT_CAPACITY;
//    pq->size = 0;

//    pq->data = calloc(pq->capacity, sizeof(struct pq_node));

    pq->positive_locker = getpid();
    pthread_mutex_init(&pq->mutex, NULL);
    init_deque(&pq->tmp_queue);

}

priority_queue *create_pqueue() {
    priority_queue *ret = malloc(sizeof(priority_queue));
    init_pqueue(ret);
    ret->size = ret->tmp_queue.size;
    ret->capacity = ret->tmp_queue.capacity;
    return ret;
}

void free_pqueue(priority_queue *pqueue) {
    pthread_mutex_destroy(&pqueue->mutex);
    free_deque(&pqueue->tmp_queue);
    free(pqueue->data);
    free(pqueue);
}

int pqueue_add(priority_queue *pq, int prio, void *val) {
    if (pq->positive_locker != getpid()) {
        pthread_mutex_lock(&pq->mutex);
        pq->positive_locker = getpid();
        int ret = add_last(&pq->tmp_queue, val);
        pthread_mutex_unlock(&pq->mutex);
        pq->size = pq->tmp_queue.size;
        pq->capacity = pq->tmp_queue.capacity;
        return ret;
    } else {
        int ret = add_last(&pq->tmp_queue, val);
        pq->size = pq->tmp_queue.size;
        pq->capacity = pq->tmp_queue.capacity;
        return ret;
    }

}

void pqueue_print(priority_queue *pq) {
    print_deque(&pq->tmp_queue);
}
